package com.bzxhh.bean;

import com.bzxhh.anno.Bean;
import com.bzxhh.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext {

    //创建map集合 放bean对象
    private Map<Class, Object> beanFactory = new HashMap<>();
    private String rootPath;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //创建有参构造函数 传递包路径 设置包扫描规则
    //当前包及其子包 哪个类有@Bean注解 把这个类通过反射实例化
    public AnnotationApplicationContext(String basePackage) {
        try {
            //com.bzxhh
            //1 把.替换成\
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //2 获取包的绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路径部分
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //属性注入
        try {
            loadDi();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1 判断当前内容是否是文件夹
        if (file.isDirectory()) {
            //2 获取文件夹里面所有内容
            File[] childrenFiles = file.listFiles();
            //3 判断文件夹里面为空 直接返回
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //4 如果文件夹不为空 遍历文件夹所有内容
            for (File childrenFile : childrenFiles) {
                //4.1 遍历得到每个File对象 继续判断 如果还是文件夹 递归
                if (childrenFile.isDirectory()) {
                    //递归
                    loadBean(childrenFile);
                } else {
                    //4.2 遍历得到的File不是文件夹 是文件
                    //4.3 得到包路径+类名称部分-字符串截取
                    String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4 判断当前文件类型是否是.class
                    if (pathWithClass.contains(".class")) {
                        //4.5 如果是.class类型 把路径\替换成. 把.class去掉
                        //com.bzxhh.service.UserServiceImpl
                        String allName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class", "");
                        //4.6 判断类上是否有注解 @Bean 如果有就实例化
                        //4.6.1 获取类的class
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2 判断不是接口
                        if (!clazz.isInterface()) {
                            //4.6.3 判断类上是否有注解 @Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                //4.6.4 实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7 把实例对象 放到map集合中去
                                //4.7.1 当前类有接口 让接口的class作为map的key
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    beanFactory.put(clazz, instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void loadDi() throws IllegalAccessException {
        //实例化的对象都在beanFactory的map集合中
        //1 遍历beanFactory的map集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2 获取map集合每个对象（value） 每个对象属性获取到
            Object object = entry.getValue();
            Class<?> clazz = object.getClass();
            //3 遍历得到的每个对象属性数组 得到每个属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //4 判断属性上面是否有@Di注解
                Di annotation = declaredField.getAnnotation(Di.class);
                if (annotation != null) {
                    //私有属性 设置可以设置值
                    declaredField.setAccessible(true);
                    //5 有注解 把对象进行注入
                    declaredField.set(object, beanFactory.get(declaredField.getType()));
                }
            }
        }
    }
}
